gtk_combo_box_append_text (GTK_COMBO_BOX (entry), "Three");
}
+
+/* A simple validating entry */
+
+#define TYPE_MASK_ENTRY (mask_entry_get_type ())
+#define MASK_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MASK_ENTRY, MaskEntry))
+#define MASK_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), TYPE_MASK_ENTRY, MaskEntryClass))
+#define IS_MASK_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MASK_ENTRY))
+#define IS_MASK_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), TYPE_MASK_ENTRY))
+#define MASK_ENTRY_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), TYPE_MASK_ENTRY, MaskEntryClass))
+
+
+typedef struct _MaskEntry MaskEntry;
+struct _MaskEntry
+{
+ GtkEntry entry;
+ gchar *mask;
+};
+
+typedef struct _MaskEntryClass MaskEntryClass;
+struct _MaskEntryClass
+{
+ GtkEntryClass parent_class;
+};
+
+
+static void mask_entry_editable_init (GtkEditableClass *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MaskEntry, mask_entry, GTK_TYPE_ENTRY,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
+ mask_entry_editable_init));
+
+
+static void
+mask_entry_set_background (MaskEntry *entry)
+{
+ static const GdkColor error_color = { 0, 65535, 60000, 60000 };
+
+ if (entry->mask)
+ {
+ if (!g_regex_match_simple (entry->mask, gtk_entry_get_text (GTK_ENTRY (entry)), 0, 0))
+ {
+ gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, &error_color);
+ return;
+ }
+ }
+
+ gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, NULL);
+}
+
+
+static void
+mask_entry_changed (GtkEditable *editable)
+{
+ mask_entry_set_background (MASK_ENTRY (editable));
+}
+
+
+static void
+mask_entry_init (MaskEntry *entry)
+{
+ entry->mask = NULL;
+}
+
+
+static void
+mask_entry_class_init (MaskEntryClass *klass)
+{ }
+
+
+static void
+mask_entry_editable_init (GtkEditableClass *iface)
+{
+ iface->changed = mask_entry_changed;
+}
+
+
GtkWidget *
do_combobox (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
- GtkWidget *vbox, *frame, *box, *combo;
+ GtkWidget *vbox, *frame, *box, *combo, *entry;
GtkTreeModel *model;
GtkCellRenderer *renderer;
GtkTreePath *path;
gtk_tree_path_free (path);
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
- /* A GtkComboBoxEntry
+ /* A GtkComboBoxEntry with validation.
*/
frame = gtk_frame_new ("Editable");
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
fill_combo_entry (combo);
gtk_container_add (GTK_CONTAINER (box), combo);
+ entry = g_object_new (TYPE_MASK_ENTRY, NULL);
+ MASK_ENTRY (entry)->mask = "^([0-9]*|One|Two|2\302\275|Three)$";
+
+ gtk_container_remove (GTK_CONTAINER (combo), GTK_BIN (combo)->child);
+ gtk_container_add (GTK_CONTAINER (combo), entry);
+
}
if (!GTK_WIDGET_VISIBLE (window))
<para>
In contrast to a #GtkComboBox, the underlying model of a #GtkComboBoxEntry
must always have a text column (see gtk_combo_box_entry_set_text_column()),
-and the entry will show the content of the text column in the selected row. To
-get the text from the entry, use gtk_combo_box_get_active_text().
+and the entry will show the content of the text column in the selected row.
+To get the text from the entry, use gtk_combo_box_get_active_text().
</para>
-<para>The changed signal will be emitted while typing into a GtkComboBoxEntry,
+<para>
+The changed signal will be emitted while typing into a GtkComboBoxEntry,
as well as when selecting an item from the GtkComboBoxEntry's list. Use
gtk_combo_box_get_active() or gtk_combo_box_get_active_iter() to discover
whether an item was actually selected from the list.
</para>
-<para>Connect to the activate signal of the GtkEntry (use gtk_bin_get_child()) to
-detect when the user actually finishes entering text.</para>
+<para>
+Connect to the activate signal of the GtkEntry (use gtk_bin_get_child())
+to detect when the user actually finishes entering text.
+</para>
<para>
-The convenience API to construct simple text-only #GtkComboBox<!-- -->es can
-also be used with #GtkComboBoxEntry<!-- -->s which have been constructed
+The convenience API to construct simple text-only #GtkComboBox<!-- -->es
+can also be used with #GtkComboBoxEntry<!-- -->s which have been constructed
with gtk_combo_box_entry_new_text().
</para>
+<para>
+If you have special needs that go beyond a simple entry (e.g. input validation),
+it is possible to replace the child entry by a different widget using
+gtk_container_remove() and gtk_container_add().
+</para>
+
<!-- ##### SECTION See_Also ##### -->
<para>
#GtkComboBox
struct _GtkComboBoxEntryPrivate
{
- GtkWidget *entry;
-
GtkCellRenderer *text_renderer;
gint text_column;
};
guint prop_id,
GValue *value,
GParamSpec *pspec);
+static void gtk_combo_box_entry_add (GtkContainer *container,
+ GtkWidget *child);
+static void gtk_combo_box_entry_remove (GtkContainer *container,
+ GtkWidget *child);
static gchar *gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box);
static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
GtkComboBoxClass *combo_class;
object_class = (GObjectClass *)klass;
widget_class->mnemonic_activate = gtk_combo_box_entry_mnemonic_activate;
widget_class->grab_focus = gtk_combo_box_entry_grab_focus;
+ container_class = (GtkContainerClass *)klass;
+ container_class->add = gtk_combo_box_entry_add;
+ container_class->remove = gtk_combo_box_entry_remove;
+
combo_class = (GtkComboBoxClass *)klass;
combo_class->get_active_text = gtk_combo_box_entry_get_active_text;
static void
gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box)
{
+ GtkWidget *entry;
+
entry_box->priv = GTK_COMBO_BOX_ENTRY_GET_PRIVATE (entry_box);
entry_box->priv->text_column = -1;
- entry_box->priv->entry = gtk_entry_new ();
- /* this flag is a hack to tell the entry to fill its allocation.
- */
- GTK_ENTRY (entry_box->priv->entry)->is_cell_renderer = TRUE;
- gtk_container_add (GTK_CONTAINER (entry_box), entry_box->priv->entry);
- gtk_widget_show (entry_box->priv->entry);
+ entry = gtk_entry_new ();
+ gtk_widget_show (entry);
+ gtk_container_add (GTK_CONTAINER (entry_box), entry);
entry_box->priv->text_renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry_box),
gtk_combo_box_set_active (GTK_COMBO_BOX (entry_box), -1);
- g_signal_connect (entry_box->priv->entry, "changed",
- G_CALLBACK (gtk_combo_box_entry_contents_changed),
- entry_box);
g_signal_connect (entry_box, "changed",
G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
- has_frame_changed (entry_box, NULL, NULL);
g_signal_connect (entry_box, "notify::has-frame", G_CALLBACK (has_frame_changed), NULL);
}
}
}
+static void
+gtk_combo_box_entry_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (container);
+
+ if (!GTK_IS_ENTRY (child))
+ {
+ g_warning ("Attempting to add a widget with type %s to a GtkComboBoxEntry "
+ "(need an instance of GtkEntry or of a subclass)",
+ G_OBJECT_TYPE_NAME (child));
+ return;
+ }
+
+ GTK_CONTAINER_CLASS (gtk_combo_box_entry_parent_class)->add (container, child);
+
+ /* this flag is a hack to tell the entry to fill its allocation.
+ */
+ GTK_ENTRY (child)->is_cell_renderer = TRUE;
+
+ g_signal_connect (child, "changed",
+ G_CALLBACK (gtk_combo_box_entry_contents_changed),
+ entry_box);
+ has_frame_changed (entry_box, NULL, NULL);
+}
+
+static void
+gtk_combo_box_entry_remove (GtkContainer *container,
+ GtkWidget *child)
+{
+ if (child && child == GTK_BIN (container)->child)
+ {
+ g_signal_handlers_disconnect_by_func (child,
+ gtk_combo_box_entry_contents_changed,
+ container);
+ GTK_ENTRY (child)->is_cell_renderer = FALSE;
+ }
+
+ GTK_CONTAINER_CLASS (gtk_combo_box_entry_parent_class)->remove (container, child);
+}
+
static void
gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
gpointer user_data)
if (gtk_combo_box_get_active_iter (combo_box, &iter))
{
- g_signal_handlers_block_by_func (entry_box->priv->entry,
- gtk_combo_box_entry_contents_changed,
- combo_box);
-
- model = gtk_combo_box_get_model (combo_box);
-
- gtk_tree_model_get (model, &iter,
- entry_box->priv->text_column, &str,
- -1);
- gtk_entry_set_text (GTK_ENTRY (entry_box->priv->entry), str);
- g_free (str);
-
- g_signal_handlers_unblock_by_func (entry_box->priv->entry,
- gtk_combo_box_entry_contents_changed,
- combo_box);
+ GtkEntry *entry = GTK_ENTRY (GTK_BIN (combo_box)->child);
+
+ if (entry)
+ {
+ g_signal_handlers_block_by_func (entry,
+ gtk_combo_box_entry_contents_changed,
+ combo_box);
+
+ model = gtk_combo_box_get_model (combo_box);
+
+ gtk_tree_model_get (model, &iter,
+ entry_box->priv->text_column, &str,
+ -1);
+ gtk_entry_set_text (entry, str);
+ g_free (str);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gtk_combo_box_entry_contents_changed,
+ combo_box);
+ }
}
}
GParamSpec *pspec,
gpointer data)
{
- gboolean has_frame;
+ if (GTK_BIN (entry_box)->child)
+ {
+ gboolean has_frame;
- g_object_get (entry_box, "has-frame", &has_frame, NULL);
+ g_object_get (entry_box, "has-frame", &has_frame, NULL);
- gtk_entry_set_has_frame (GTK_ENTRY (entry_box->priv->entry), has_frame);
+ gtk_entry_set_has_frame (GTK_ENTRY (GTK_BIN (entry_box)->child), has_frame);
+ }
}
static void
gtk_combo_box_entry_mnemonic_activate (GtkWidget *widget,
gboolean group_cycling)
{
- GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (widget);
+ GtkBin *entry_box = GTK_BIN (widget);
- gtk_widget_grab_focus (entry_box->priv->entry);
+ if (entry_box->child)
+ gtk_widget_grab_focus (entry_box->child);
return TRUE;
}
static void
gtk_combo_box_entry_grab_focus (GtkWidget *widget)
{
- GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (widget);
+ GtkBin *entry_box = GTK_BIN (widget);
- gtk_widget_grab_focus (entry_box->priv->entry);
+ if (entry_box->child)
+ gtk_widget_grab_focus (entry_box->child);
}
static gchar *
gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box)
{
- GtkComboBoxEntry *combo = GTK_COMBO_BOX_ENTRY (combo_box);
+ GtkBin *combo = GTK_BIN (combo_box);
- if (combo->priv->entry)
- return g_strdup (gtk_entry_get_text (GTK_ENTRY (combo->priv->entry)));
+ if (combo->child)
+ return g_strdup (gtk_entry_get_text (GTK_ENTRY (combo->child)));
return NULL;
}